/**
 * Copyright 2018-2020 stylefeng & fengshuonan (https://gitee.com/stylefeng)
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.stylefeng.guns.sys.modular.api.controller;

import cn.hutool.core.codec.Base64;
import cn.stylefeng.guns.base.shiro.ShiroUser;
import cn.stylefeng.guns.sys.core.shiro.ShiroKit;
import cn.stylefeng.guns.sys.core.util.JwtTokenUtil;
import cn.stylefeng.guns.sys.modular.api.sdk.HttpKit;
import cn.stylefeng.guns.sys.modular.api.sdk.PaymentKit;
import cn.stylefeng.guns.sys.modular.api.sdk.WXPayUtil;
import cn.stylefeng.guns.sys.modular.api.sdk.WxProgramPayConfig;
import cn.stylefeng.guns.sys.modular.system.entity.User;
import cn.stylefeng.guns.sys.modular.system.mapper.UserMapper;
import cn.stylefeng.guns.sys.modular.tao.entity.Member;
import cn.stylefeng.guns.sys.modular.tao.entity.MemberSpecs;
import cn.stylefeng.guns.sys.modular.tao.entity.RechargeRecord;
import cn.stylefeng.guns.sys.modular.tao.entity.WxUser;
import cn.stylefeng.guns.sys.modular.tao.model.params.BillParam;
import cn.stylefeng.guns.sys.modular.tao.model.params.RechargeRecordParam;
import cn.stylefeng.guns.sys.modular.tao.service.*;
import cn.stylefeng.roses.core.base.controller.BaseController;
import cn.stylefeng.roses.core.reqres.response.ErrorResponseData;
import cn.stylefeng.roses.core.reqres.response.ResponseData;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.util.ByteSource;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 接口控制器提供
 *
 * @author stylefeng
 * @Date 2018/7/20 23:39
 */
@Slf4j
@RestController
@RequestMapping("/gunsApi")
public class ApiController extends BaseController {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RechargeRecordService rechargeRecordService;
    @Autowired
    private MemberService memberService;
    @Autowired
    private MemberSpecsService memberSpecsService;
    @Autowired
    private WxUserService wxUserService;
    @Autowired
    private BillService billService;

    private static String unifiedOrderUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    /**
     * 成功的标识
     */
    private final static String SUCCESS = "SUCCESS";

    /**
     * 返回状态码的变量名
     */
    private final static String RETURN_CODE = "RETURN_CODE";

    /**
     * api登录接口，通过账号密码获取token
     */
    @RequestMapping("/auth")
    public Object auth(@RequestParam("username") String username,
                       @RequestParam("password") String password) {

        //封装请求账号密码为shiro可验证的token
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password.toCharArray());

        //获取数据库中的账号密码，准备比对
        User user = userMapper.getByAccount(username);

        String credentials = user.getPassword();
        String salt = user.getSalt();
        ByteSource credentialsSalt = new Md5Hash(salt);
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
                new ShiroUser(), credentials, credentialsSalt, "");

        //校验用户账号密码
        HashedCredentialsMatcher md5CredentialsMatcher = new HashedCredentialsMatcher();
        md5CredentialsMatcher.setHashAlgorithmName(ShiroKit.hashAlgorithmName);
        md5CredentialsMatcher.setHashIterations(ShiroKit.hashIterations);
        boolean passwordTrueFlag = md5CredentialsMatcher.doCredentialsMatch(
                usernamePasswordToken, simpleAuthenticationInfo);

        if (passwordTrueFlag) {
            HashMap<String, Object> result = new HashMap<>();
            result.put("token", JwtTokenUtil.generateToken(String.valueOf(user.getUserId())));
            return result;
        } else {
            return new ErrorResponseData(500, "账号密码错误！");
        }
    }

    /**
     * 测试接口是否走鉴权
     */
    @RequestMapping(value = "/test", method = RequestMethod.GET)
    public Object test() {
        return SUCCESS_TIP;
    }

    /**
     * 微信支付
     */
    public static Object wxPay(String openid, String outTradeNo,BigDecimal money)  {
        Map<String, String> reqParams = new HashMap<>();
        //微信分配的小程序ID
        reqParams.put("appid", WxProgramPayConfig.APPID);
        //微信支付分配的商户号
        reqParams.put("mch_id", WxProgramPayConfig.MCH_ID);
        //随机字符串
        reqParams.put("nonce_str", System.currentTimeMillis() / 1000 + "");
        //签名类型
        reqParams.put("sign_type", "MD5");
        //充值订单 商品描述
        reqParams.put("body", "");
        //商户订单号
        reqParams.put("out_trade_no", outTradeNo);
        //订单总金额，单位为分
        reqParams.put("total_fee", money.multiply(BigDecimal.valueOf(100)).intValue() + "");
        //终端IP
        reqParams.put("spbill_create_ip", "127.0.0.1");
        //通知地址
        reqParams.put("notify_url", WxProgramPayConfig.NOTIFY_URL);
        //交易类型
        reqParams.put("trade_type", "JSAPI");
        //用户标识
        reqParams.put("openid", openid);
 /*
            调用支付定义下单API,返回预付单信息 prepay_id
         */
        String xmlResult = ApiController.pushOrder(reqParams);
        Map<String, String> result = PaymentKit.xmlToMap(xmlResult);
        //预付单信息
        String prepay_id = result.get("prepay_id");
 /*
            小程序调起支付数据签名
         */
        Map<String, String> packageParams = new HashMap<>();
        packageParams.put("appId", WxProgramPayConfig.APPID);
        packageParams.put("timeStamp", System.currentTimeMillis() / 1000 + "");
        packageParams.put("nonceStr", System.currentTimeMillis() + "");
        packageParams.put("package", "prepay_id=" + prepay_id);
        packageParams.put("signType", "MD5");
        String packageSign = null;
        try {
            packageSign = WXPayUtil.generateSignature(packageParams, WxProgramPayConfig.KEY);
        } catch (Exception e) {
            e.printStackTrace();
        }
        packageParams.put("paySign", packageSign);
        return packageParams;
    }

    /**
     * 统一下单
     *
     * @param params 参数map
     * @return String
     */
    public static String pushOrder(Map<String, String> params) {
        return HttpKit.post(unifiedOrderUrl, PaymentKit.toXml(params));
    }

    /**
     * 根据 临时登录凭证获取openId
     * 文档:https://developers.weixin.qq.com/miniprogram/dev/api/code2Session.html
     *
     * @param code
     * @return
     * @author majker
     */
    public static String getOpenIdByCode(String code) {
        log.info("获取code成功!{}", code);
        //登录凭证校验
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + WxProgramPayConfig.APPID + "&secret=" + WxProgramPayConfig.SECRET + "&js_code=" + code + "&grant_type=authorization_code";
        //发送请求给微信后端
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet(url);
        InputStream inputStream = null;
        CloseableHttpResponse httpResponse = null;
        StringBuilder result = new StringBuilder();
        String openId = null;
        try {
            httpResponse = httpClient.execute(httpGet);
            HttpEntity entity = httpResponse.getEntity();
            inputStream = entity.getContent();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String line = "";
            while ((line = bufferedReader.readLine()) != null) {
                //这里需要使用fastjson来提取一下内容
                System.out.println(line);
                JSONObject jsonObject = JSON.parseObject(line);
                openId = jsonObject.getString("openid");
                String sessionKey = jsonObject.getString("session_key");
                log.info("openId={},sessionKey={}", openId, sessionKey);
            }
        } catch (IOException e) {
            log.error("获取openId失败" + e.getMessage());
        }
        return openId;
    }

    public static String getSessionKeyByCode(String code) {
        log.info("获取code成功!{}", code);
        //登录凭证校验
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + WxProgramPayConfig.APPID + "&secret=" + WxProgramPayConfig.SECRET + "&js_code=" + code + "&grant_type=authorization_code";
        //发送请求给微信后端
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet(url);
        InputStream inputStream = null;
        CloseableHttpResponse httpResponse = null;
        StringBuilder result = new StringBuilder();
        String openId = null;
        String sessionKey=null;
        try {
            httpResponse = httpClient.execute(httpGet);
            HttpEntity entity = httpResponse.getEntity();
            inputStream = entity.getContent();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String line = "";
            while ((line = bufferedReader.readLine()) != null) {
                //这里需要使用fastjson来提取一下内容
                System.out.println(line);
                JSONObject jsonObject = JSON.parseObject(line);
                openId = jsonObject.getString("openid");
                sessionKey = jsonObject.getString("session_key");
                log.info("openId={},sessionKey={}", openId, sessionKey);
            }
        } catch (IOException e) {
            log.error("获取openId失败" + e.getMessage());
        }
        return sessionKey;
    }

    /**
     * 功能描述: <小程序回调>
     *
     * @return:
     * @auther: majker
     * @date: 2019/3/10
     **/
    @RequestMapping("/wxProPayNotify")
    public void wxProPayNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
        log.info("进入微信小程序支付回调");
        String xmlMsg = HttpKit.readData(request);
        log.info("微信小程序通知信息" + xmlMsg);
        Map<String, String> resultMap = PaymentKit.xmlToMap(xmlMsg);
        if (resultMap.get(RETURN_CODE).equals(SUCCESS)) {
            String orderNo = resultMap.get("out_trade_no");
            log.info("微信小程序支付成功,订单号{}", orderNo);
            /**
             *  通过订单号 修改数据库中的记录
             */
            //首先改变该订单状态
            RechargeRecordParam rechargeRecord = rechargeRecordService.getRechargeRecordById(orderNo);
            rechargeRecord.setState(1);
            rechargeRecordService.update(rechargeRecord);
            //获取用户
            WxUser userById = wxUserService.getUserById(rechargeRecord.getUserId());
            //获取会员规格
            MemberSpecs memberSpecs= memberSpecsService.getById(rechargeRecord.getId());
            //设置用户会员属性表
             Member member =   memberService.getMemberById(rechargeRecord.getUserId());
             //用户第一次充值会员
             if(member==null){
                 //设置会员表
                 member.setId(Long.valueOf(rechargeRecord.getUserId()));
                 member.setCreateTime(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
                 member.setUpdateTime(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
                 Calendar cal = Calendar.getInstance();
                 cal.setTime(new Date());//设置起时间
                 cal.add(Calendar.YEAR, 1);//增加1年
                 member.setEndTime(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(cal.getTime()));
                 member.setDelSign("0");
                 member.setCommission(new BigDecimal("0"));
                 member.setPurchaseQuota(memberSpecs.getVipLimited());
                 member.setIntegral(0l);
                 member.setDiscountAmount(new BigDecimal("0"));
                 memberService.add(member);
                 //用户等级设置在用户表中
                 userById.setMemberGrade(memberSpecs.getVipLevel());
                 wxUserService.update(userById);
                 //设置流水表
                 BillParam billParam = new BillParam();
                 billParam.setUserId(userById.getId());
                 billParam.setPay(memberSpecs.getVipPrice());
                 billParam.setPayTime(new Date());
                 billParam.setPayWay(0);
                 billParam.setDetails(2);
                 billParam.setStatus(1);
                 billParam.setType(1);
                 billParam.setUnit("人民币");
                 billParam.setOrderId(orderNo);
                 billService.add(billParam);
                 //如果有推荐人  那么给推荐人增加佣金
                 if(userById.getPromoterId()!=null&&!"".equals(userById.getPromoterId())){
                     //推荐人增加佣金
                     Member promoter = memberService.getMemberById(userById.getPromoterId().toString());
                     promoter.setCommission(promoter.getCommission().add(memberSpecs.getCommission()));
                     memberService.update(promoter);
                     //获取佣金流水表
                     BillParam billParams = new BillParam();
                     billParams.setUnit("佣金");
                     billParams.setUserId(userById.getPromoterId());
                     billParams.setPay(memberSpecs.getCommission());
                     billParams.setDetails(0);
                     billParams.setPayTime(new Date());
                     billParams.setStatus(1);
                     billService.add(billParams);
                 }
             }else if(userById.getMemberGrade().equals(memberSpecs.getVipLevel())){
                 //如果是续费的时候 只要把结束的时间延长即可
                 Calendar cal = Calendar.getInstance();
                 //设置起时间 在原来的时间上累加
                 cal.setTime(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(member.getEndTime()));
                 cal.add(Calendar.YEAR, 1);//增加1年
                 member.setUpdateTime(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
                 member.setEndTime(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(cal.getTime()));
                 memberService.update(member);
                 //设置流水表
                 BillParam billParam = new BillParam();
                 billParam.setUserId(userById.getId());
                 billParam.setPay(memberSpecs.getVipPrice());
                 billParam.setPayTime(new Date());
                 billParam.setPayWay(0);
                 billParam.setDetails(2);
                 billParam.setStatus(1);
                 billParam.setType(1);
                 billParam.setUnit("人民币");
                 billParam.setOrderId(orderNo);
                 billService.add(billParam);
             }else if(Integer.parseInt(userById.getMemberGrade())<Integer.parseInt(memberSpecs.getVipLevel())){
                //如果是升级的话  除了购买额度变化  其他不变
                 member.setUpdateTime(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
                 member.setPurchaseQuota(memberSpecs.getVipLimited());
                 memberService.update(member);
                 //用户等级设置在用户表中
                 userById.setMemberGrade(memberSpecs.getVipLevel());
                 wxUserService.update(userById);
                 //设置流水表
                 BillParam billParam = new BillParam();
                 //获取原来的会员规格
                 MemberSpecs memberSpecss =   memberSpecsService.getMemberSpecsByLevel(userById.getMemberGrade());
                 billParam.setUserId(userById.getId());
                 billParam.setPay(memberSpecs.getVipPrice().subtract(memberSpecss.getVipPrice()));
                 billParam.setPayTime(new Date());
                 billParam.setPayWay(0);
                 billParam.setDetails(2);
                 billParam.setStatus(1);
                 billParam.setType(1);
                 billParam.setUnit("人民币");
                 billParam.setOrderId(orderNo);
                 billService.add(billParam);
             }else if(member!=null&&userById.getMemberGrade().equals("0")){
                 //会员过期的再次续费
                 Calendar cal = Calendar.getInstance();
                 //重新开始计时
                 cal.setTime(new Date());
                 cal.add(Calendar.YEAR, 1);//增加1年
                 member.setUpdateTime(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
                 member.setEndTime(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(cal.getTime()));
                 member.setPurchaseQuota(memberSpecs.getVipLimited());
                 memberService.update(member);
                 //用户等级设置在用户表中
                 userById.setMemberGrade(memberSpecs.getVipLevel());
                 wxUserService.update(userById);
                 //设置流水表
                 BillParam billParam = new BillParam();
                 billParam.setUserId(userById.getId());
                 billParam.setPay(memberSpecs.getVipPrice());
                 billParam.setPayTime(new Date());
                 billParam.setPayWay(0);
                 billParam.setDetails(2);
                 billParam.setStatus(1);
                 billParam.setType(1);
                 billParam.setUnit("人民币");
                 billParam.setOrderId(orderNo);
                 billService.add(billParam);
             }
        }
        String result = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
        try {
            response.getWriter().write(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取微信手机号
     */
    public static String getWxPhoneNumber( String code,String encryptedData, String iv){
        String sessionKey = null;
        String phoneNumber = null;
        ResponseData data = new ResponseData();
        try {
            sessionKey = ApiController.getSessionKeyByCode(code);
            // 被加密的数据
            byte[] dataByte = Base64.decode(encryptedData);
            // 加密秘钥
            byte[] keyByte = Base64.decode(sessionKey);
            // 偏移量
            byte[] ivByte = Base64.decode(iv);
            try {
                // 如果密钥不足16位，那么就补足.  这个if 中的内容很重要
                int base = 16;
                if (keyByte.length % base != 0) {
                    int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                    byte[] temp = new byte[groups * base];
                    Arrays.fill(temp, (byte) 0);
                    System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                    keyByte = temp;
                }
                // 初始化
                Security.addProvider(new BouncyCastleProvider());
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
                SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
                AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
                parameters.init(new IvParameterSpec(ivByte));
                cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
                byte[] resultByte = cipher.doFinal(dataByte);
                if (null != resultByte && resultByte.length > 0) {
                    String result = new String(resultByte, "UTF-8");
                    JSONObject jsaon =  JSONObject.parseObject(result);
                    phoneNumber = jsaon.getString("phoneNumber");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return phoneNumber;
    }

    /**
     * @return  access_token
     * @throws Exception
     */
    public static String getAccessToken() throws Exception {
        String apiKey = "";//小程序id
        String secretKey = "";//小程序密钥
        String requestUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + apiKey + "&secret=" + secretKey;
        URL url = new URL(requestUrl);
        // 打开和URL之间的连接
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        // 设置通用的请求属性
        connection.setRequestProperty("Content-Type", "application/json");
        connection.setRequestProperty("Connection", "Keep-Alive");
        connection.setUseCaches(false);
        connection.setDoOutput(true);
        connection.setDoInput(true);
        // 得到请求的输出流对象
        DataOutputStream out = new DataOutputStream(connection.getOutputStream());
        out.writeBytes("");
        out.flush();
        out.close();
        // 建立实际的连接
        connection.connect();
        // 定义 BufferedReader输入流来读取URL的响应
        BufferedReader in = null;
        if (requestUrl.contains("nlp"))
            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "GBK"));
        else
            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
        String result = "";
        String getLine;
        while ((getLine = in.readLine()) != null) {
            result += getLine;
        }
        in.close();
        JSONObject jsonObject = JSON.parseObject(result);
        String accesstoken = jsonObject.getString("access_token");
        return accesstoken;
    }

    /**
     * 生成带参小程序二维码
     * @param
     *
     * @param accessToken	token
     */
    public static String postMiniqrQr(String accessToken,String userId) {
        String fileUrl = "http://192.168.0.106:8089/D:/test";
        String pictureUrl = null;
        try {
            URL url = new URL("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + accessToken);
            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            // conn.setConnectTimeout(10000);//连接超时 单位毫秒
            // conn.setReadTimeout(2000);//读取超时 单位毫秒
            // 发送POST请求必须设置如下两行
            httpURLConnection.setDoOutput(true); // 打开写入属性
            httpURLConnection.setDoInput(true); // 打开读取属性
            httpURLConnection.setRequestMethod("POST");// 提交方式
            // 不得不说一下这个提交方式转换！！真的坑。。改了好长时间！！一定要记得加响应头
            httpURLConnection.setRequestProperty("Content-Type", "application/x-javascript; charset=UTF-8");// 设置响应头
            // 获取URLConnection对象对应的输出流
            PrintWriter printWriter = new PrintWriter(httpURLConnection.getOutputStream());
            // 发送请求参数
            JSONObject paramJson = new JSONObject();
            paramJson.put("scene", userId); // 你要放的内容
            paramJson.put("path", "pages/index/index");
            paramJson.put("width", 430); // 宽度
            paramJson.put("auto_color", true);
            printWriter.write(paramJson.toString());
            // flush输出流的缓冲
            printWriter.flush();
            BufferedInputStream bis = new BufferedInputStream(httpURLConnection.getInputStream());
            //创建一个空文件
            OutputStream os = new FileOutputStream(new File(fileUrl+userId+".jpg"));
            pictureUrl = fileUrl+userId+".jpg";
            //ByteArrayOutputStream os = new ByteArrayOutputStream();
            int len;
            byte[] arr = new byte[1024];
            while ((len = bis.read(arr)) != -1) {
                os.write(arr, 0, len);
                os.flush();
            }
            os.close();
            // 上传云储存
            //InputStream is = new ByteArrayInputStream(os.toByteArray());
            //retMap = UploadUtils.upload(is);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return pictureUrl;
    }
}